Use the Domain Formula as a Feature

Hybrid analytical-ML modeling for delivery time prediction

Overview

This project predicts delivery time for a logistics platform in Nairobi. The key modeling decision was not to choose between a hand-crafted domain formula and a machine learning model, but to combine both.

A first-principles estimate of duration was constructed from route distance and rider historical speed. Instead of using that estimate as the final prediction, it was fed into XGBoost as a feature. The model then learned the correction from contextual variables such as pickup delay, geography, rider history, and time-of-day effects.

The result is a hybrid analytical-ML design: the formula provides a structured prior, and the model learns where reality systematically deviates from that prior.

Architecture

Hybrid analytical-ML modeling diagram
πŸ“‹ View detailed text-based architecture diagram
                  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                  β”‚  Raw Delivery Context              β”‚
                  β”‚  - distance                        β”‚
                  β”‚  - rider history                   β”‚
                  β”‚  - pickup / arrival timestamps     β”‚
                  β”‚  - location coordinates            β”‚
                  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                   β”‚
                                   β–Ό
               β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
               β”‚ Analytical Baseline Construction           β”‚
               β”‚ expected_duration β‰ˆ distance / rider_speed β”‚
               β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                β”‚
                                β–Ό
               β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
               β”‚ Baseline-Derived Features                  β”‚
               β”‚ - dur_estim                                β”‚
               β”‚ - dur_estim_avg                            β”‚
               β”‚ - dur_nor                                  β”‚
               β”‚ - imp                                      β”‚
               β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                β”‚
          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
          β–Ό                     β–Ό                              β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Rider Priors     β”‚  β”‚ Geographic Context β”‚      β”‚ Operational Timing β”‚
β”‚ - speed_med      β”‚  β”‚ - pickup clusters  β”‚      β”‚ - plac_confir      β”‚
β”‚ - speed_avg      β”‚  β”‚ - dest clusters    β”‚      β”‚ - confir_arriv     β”‚
β”‚ - best_rider     β”‚  β”‚ - haversine        β”‚      β”‚ - arriv_pick       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
          β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                 β”‚
                                 β–Ό
                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                    β”‚      XGBoost Regressor     β”‚
                    β”‚  learns residual structure β”‚
                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                   β”‚
                                   β–Ό
                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                    β”‚ Final Delivery Time Pred.  β”‚
                    β”‚ formula prior + correction β”‚
                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
          

The Core Idea

In many applied regression problems, part of the target can already be approximated using domain logic. In this case, travel duration can be roughly estimated from distance and rider historical speed.

The key decision was to avoid two common extremes:

  • using the formula alone as the final prediction
  • ignoring the formula and forcing the model to learn that relationship from scratch

Instead, the analytical estimate was treated as a structured baseline and injected into the feature space. This shifts the learner’s role: rather than modeling everything from zero, it focuses on residual variation caused by traffic, local geography, pickup friction, rider differences, and operational noise.

Analytical Baseline Features

Estimated Duration

A baseline duration estimate was computed from route distance and rider median speed, creating a first-principles approximation of travel time.

Normalized Duration

The baseline estimate was combined with median rider pickup behavior to create a more realistic operational duration proxy.

Improvement Ratio

Ratio-based features were added to express how much a given order deviates from what would normally be expected from the rider’s historical pattern.

Broader Feature Engineering

The hybrid baseline was only one part of a richer feature space built around rider behavior, geography, and operational timing.

  • Rider priors: median speed, average speed, historical duration, distance statistics, quality scores, and error flags
  • Geographic abstraction: K-means clustering on pickup and destination coordinates, followed by nearest-centroid assignment
  • Operational timing: time differences between placement, confirmation, arrival at pickup, and pickup
  • Distance consistency: haversine and route-based distance comparisons
  • Binned variables: distance, hour, rider age, rider order volume, speed profile, and score profile

Selected Code Snippets

1. Building the analytical baseline

library(dplyr)

df <- df %>%
  mutate(
    speed_med = pmin(speed_med, 48),
    arriv_pick_med = pmin(arriv_pick_med, 528),
    dis = ((dist / 1000) + Distance__KM_) / 2,
    dur_estim = dis / speed_med * 3600,
    dur_estim_avg = dis / speed_avg * 3600,
    dur_nor = arriv_pick_med + dur_estim,
    imp = dur_nor / arriv_pick
  )
        

2. Rider performance priors

rider_stats <- train %>%
  group_by(Rider_Id) %>%
  summarise(
    speed_avg = mean(speed, na.rm = TRUE),
    speed_med = median(speed, na.rm = TRUE),
    arriv_pick_med = median(arriv_pick, na.rm = TRUE),
    dist_avg = mean(Distance__KM_, na.rm = TRUE),
    dist_med = median(Distance__KM_, na.rm = TRUE),
    dur_avg = mean(Time_from_Pickup_to_Arrival, na.rm = TRUE),
    dur_med = median(Time_from_Pickup_to_Arrival, na.rm = TRUE),
    Nb_ord = n(),
    .groups = "drop"
  )
        

3. Geographic clustering

set.seed(123)

location <- bind_rows(
  train %>% transmute(lat = Pickup_Lat, long = Pickup_Long),
  train %>% transmute(lat = Destination_Lat, long = Destination_Long)
) %>%
  distinct()

k_model <- kmeans(location, centers = 10)

train$class_pick_10 <- factor(
  FNN::get.knnx(k_model$centers, train[, c("Pickup_Lat", "Pickup_Long")], 1)$nn.index[, 1]
)

train$class_dest_10 <- factor(
  FNN::get.knnx(k_model$centers, train[, c("Destination_Lat", "Destination_Long")], 1)$nn.index[, 1]
)
        

Why this matters

  • uses domain knowledge without hard-coding the final answer
  • reduces the amount of structure the model has to discover from scratch
  • lets the learner focus on residual context and operational noise
  • creates a practical hybrid analytical-ML design pattern transferable to other regression problems

Where this pattern generalizes

This same design pattern can be reused anywhere a reasonable analytical approximation already exists:

  • pricing systems with rule-based starting estimates
  • demand forecasting with baseline seasonality formulas
  • energy consumption with physical load approximations
  • manufacturing yield with process-based expected output

The principle is stable: if a formula explains part of the system, use it as structured input and let machine learning model the remainder.

Key Takeaway

Don’t ask the model to learn what the formula already knows.

A domain formula can serve as a prior feature, while the model learns the part of reality that cannot be derived analytically.

Public Repository Scope

This public version focuses on the hybrid analytical-ML design pattern, selected feature engineering components, and project presentation. It does not include competition data files.